package com.jketing.shiro.realms;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import com.jketing.entity.ams.sys.Account;
import com.jketing.entity.ams.sys.Function;
import com.jketing.entity.ams.sys.Operation;
import com.jketing.entity.ams.sys.Role;
import com.jketing.service.ams.sys.AccountService;
import com.jketing.service.ams.sys.RoleService;

/**
 * ShiroRealm shiro认证和授权Realm
 * 
 * @author  李荣华
 * @version v1.0
 * @date    2017-12-31
 * @since   Shiro 1.4.0
 */
public class ShiroRealm extends AuthorizingRealm {
	
	/**
	 *  service 	服务层自动注入
	 */
	@Autowired
	private AccountService accountService;
	/**
	 *  service 	服务层自动注入
	 */
	@Autowired
	private RoleService roleService;

	/**
	 * doGetAuthenticationInfo 复写父类认证方法
	 * 
	 * 认证时会被 shiro回调的方法
	 * 
	 * @param  AuthenticationToken token(含登录名和密码)
	 * 
	 * @return  AuthenticationInfo shiro认证信息
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		//1. 把 AuthenticationToken 转换为 UsernamePasswordToken 
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		//2. 从 UsernamePasswordToken 中来获取 username
		String username = upToken.getUsername();
		//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
		Account account = accountService.getAccountByLoginName(username);
		System.out.println("从数据库中获取 account: " + account + " 所对应的用户信息.");		
		//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
		if(account==null){
			throw new UnknownAccountException("账号不存在!");
		}
		//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常. 
		if("false".equals(account.getUseable())){
			throw new LockedAccountException("用户被锁定！");
		}
		//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
		//以下信息是从数据库中获取的.
		//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. 
		Object principal = account;
		//2). credentials: 密码. 
		Object credentials = account.getPassword(); //"fc1709d0a95a6be30bc5926fdb7f22f4";
		//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
		String realmName = getName();
		//4). 盐值. 
		ByteSource credentialsSalt = ByteSource.Util.bytes(account.getId());
		//7. 构造SimpleAuthenticationInfo认证信息对象并返回
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}

	/**
	 * doGetAuthorizationInfo 复写父类授权方法
	 * 
	 * 授权时会被 shiro回调的方法
	 * 
	 * @param  PrincipalCollection principals(授权人)
	 * 
	 * @return  AuthorizationInfo shiro授权信息
	 */
	//
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		//1. 从 PrincipalCollection 中来获取登录用户的信息
		Object principal = principals.getPrimaryPrincipal();
		Account account = (Account)principal;
		//2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)
		//角色名的集合
		Set<String> roles = new HashSet<String>();
		//权限名的集合  
        Set<String> permissions = new HashSet<String>();  
        System.out.println("从数据库中获取 account: " + account.getPerson().getName() + " 所对应的角色信息.");		
		List<Role> list = accountService.getRoles(account.getId());
		for(Role role:list) {
			roles.add(role.getCode());
		}
		String roleId = account.getDefaultRole().getId();
		System.out.println("从数据库中获取 account: " + account.getPerson().getName() + " 所对应的权限信息.");		
		List<Function> funList = roleService.getFunList(roleId);
		for(Function fun:funList) {
			if(StringUtils.isNoneBlank(fun.getStamp())){
				String mark = fun.getStamp().trim() + ":"; 
				List<Operation> butList = roleService.getButList(roleId, fun.getId());
				for(Operation item:butList) {
					if(StringUtils.isNoneBlank(item.getStamp())){
						if(!"button".equals(item.getStamp())) {
							String perm = mark + item.getStamp();
							permissions.add(perm);
						}
					}
				}
			}
		}
		for(String item:permissions) {
			if("dictionary:delete".equals(item))
				System.out.println("dictionary:delete==========================================================================");
		}
		//3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.
		//SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
        authorizationInfo.addRoles(roles);  
        authorizationInfo.addStringPermissions(permissions);  
		//4. 返回 SimpleAuthorizationInfo 对象. 
		return authorizationInfo;
	}
	
	public static void main(String[] args) {
		String hashAlgorithmName = "MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("PK0000000000000000001");;
		int hashIterations = 16;
		
		Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
		System.out.println(result);
	}
}
